/////////////////////////////////////////////////////////////////////////////
// Name:        tts_cepstral.cpp
// Purpose:     A wxWidgets wrapper around Cepstral voices (swift)
// Author:      Julian Smart
// Modified by: 
// Created:     2009-02-10
// RCS-ID:      
// Copyright:   (c) Julian Smart
// Licence:     New BSD License
/////////////////////////////////////////////////////////////////////////////

#include "wx/wx.h"

#include "wx/filename.h"
#include "wx/textfile.h"
#include "wx/datstrm.h"
#include "wx/wfstream.h"
#include "wx/dir.h"
#include "wx/log.h"

#include "tts/tts_cepstral.h"

/*
 * wxTTSCepstralHandler implementation
 */

IMPLEMENT_DYNAMIC_CLASS( wxTTSCepstralHandler, wxTTSExternalCommandHandler )

wxTTSCepstralHandler::wxTTSCepstralHandler(): wxTTSExternalCommandHandler(wxT("Cepstral"), _("Cepstral"))
{
    Init();
}

wxTTSCepstralHandler::~wxTTSCepstralHandler()
{
}

// Member initialisation
void wxTTSCepstralHandler::Init()
{
    // Initialize generic properties

    SetProgramLocation(wxT("swift"));
    SetVoice(wxT("")); // ?? No concept of default voice since can install any voice
    SetSpeed(170);      // Speed in words per minute, 80-390
    SetVolume(50);
    SetPitch(50);       // 0-100
    SetVoiceVariant(wxT("None"));

    SetProperty(wxTTS_PROP_INPUT_FORMAT, wxTTS_PROP_INPUT_FORMAT_TEXT);
    SetProperty(wxTTS_PROP_NO_SUBSTITUTIONS, false);
    SetProperty(wxTTS_PROP_SIMPLIFY_PUNCTUATION, false);
    SetProperty(wxTTS_PROP_TEXT_ENCODING, wxString(wxT("utf-8")));
    SetProperty(wxTTS_PROP_ENGINE_WEBSITE, wxString(wxT("http://www.cepstral.com")));
    SetProperty(wxTTS_PROP_PRIORITY, 0);

    // Initialize eSpeak properties
    // None yet.
}

// Speak the text
bool wxTTSCepstralHandler::Speak(const wxString& text, int options)
{
    return DoSpeak(text, false, options);
}

// Speak the file
bool wxTTSCepstralHandler::SpeakFile(const wxString& filename, int options, const wxString& waveFilename)
{
    return DoSpeak(filename, true, options, waveFilename);
}

// Speak the text or file
bool wxTTSCepstralHandler::DoSpeak(const wxString& textOrFilename, bool isFilename, int options, const wxString& waveFilename)
{
    // Avoid dangerous reentrancy
    if (IsStopping())
        return false;

    if (IsSpeaking())
    {
        Stop();
    }

    DeleteTempFile();

    SetOptions(options);

    m_errorCode = 0;
    m_pid = 0;

    // Get the flags - and always simplify punctuation
    int mandatoryOptions = wxTTS_TRANSFORM_OPTIONS_SIMPLIFY_PUNCTUATION;

    // if (GetPropertyBool(wxTTS_PROP_REMOVE_XML_PREAMBLE) || GetPropertyBool(wxTTS_PROP_INTERPRET_HTML))
    //     mandatoryOptions |= wxTTS_TRANSFORM_OPTIONS_REMOVE_XML_PREAMBLE;

    int transformationFlags = CreateTransformerOptions(mandatoryOptions);
    
    wxString cmd = wxT("\"") + GetProgramLocation() + wxT("\"");

    if (!GetVoice().IsEmpty())
        cmd << wxT(" -n ") << GetVoice();

    if (GetSpeed() != -1)
        cmd << wxT(" -p \"speech/rate=") << GetSpeed() << wxT("\"");

    if (GetVolume() != -1)
        cmd << wxT(" -p \"audio/volume=") << GetVolume() << wxT("\"");

    if (GetPitch() != -1)
        cmd << wxT(" -p \"speech/pitch/shift=") << (double) (GetPitch()/50.0) << wxT("\"");

    // TODO: text, phone

    wxString inputFormat = GetPropertyString(wxTTS_PROP_INPUT_FORMAT);
    if (inputFormat == wxTTS_PROP_INPUT_FORMAT_HTML)
    {
        transformationFlags |= (wxTTS_TRANSFORM_HTML_TO_SSML|wxTTS_TRANSFORM_SPEAK_ORDERED_LIST);
        cmd << wxT(" -m ssml");
    }
    else if (inputFormat == wxTTS_PROP_INPUT_FORMAT_SSML || inputFormat == wxTTS_PROP_INPUT_FORMAT_NATIVE)
    {
        transformationFlags |= wxTTS_TRANSFORM_PASS_RAW_TEXT;
        cmd << wxT(" -m ssml");
    }
    else
    {
        transformationFlags |= wxTTS_TRANSFORM_TEXT_TO_SSML;
        cmd << wxT(" -m ssml");
    }

    if (HasProperty(wxTTS_PROP_TEXT_ENCODING))
        cmd << wxT(" -e ") << GetPropertyString(wxTTS_PROP_TEXT_ENCODING);

    if (isFilename)
    {
        if (!waveFilename.IsEmpty())
            cmd << wxT(" -o \"") << waveFilename << wxT("\"");

        // First do some substitutions, if we have a transformer.
        wxString filename = DoTransformation(textOrFilename, transformationFlags);

        cmd << wxT(" -f \"") << filename << wxT("\"");
    }
    else
    {
        wxString text(textOrFilename);
        if (GetTransformer())
            GetTransformer()->TransformString(textOrFilename, text, transformationFlags);

        // Create a buffer containing UTF-8 characters.
        // Is this necessary?
        char *buf = new char[text.Length()*2+1];
        strcpy(buf, (const char*) text.mb_str(wxConvUTF8));
        cmd << wxT(" \"") << wxString::FromAscii(buf) << wxT("\"");

        delete[] buf;
    }

    return Launch(cmd, options);
}

// Get a list of the available voices
wxArrayString wxTTSCepstralHandler::GetAvailableVoices() const
{
    wxString dataDir = GetPropertyString(wxTTS_PROP_DATA_LOCATION);
    if (!dataDir.IsEmpty())
        return GetAvailableVoicesForDir(dataDir);
    else
        return wxArrayString();
}

// Get a list of the available voice variants
wxArrayString wxTTSCepstralHandler::GetAvailableVoiceVariants() const
{
    wxArrayString variants;

    return variants;
}

// Get a list of the available voices, given the voices directory
wxArrayString wxTTSCepstralHandler::GetAvailableVoicesForDir(const wxString& voicesDir)
{
    wxArrayString voices;
    wxArrayString dirs;

    if (!voicesDir.IsEmpty() && wxDirExists(voicesDir))
    {
        wxDir dir(voicesDir);
        wxArrayString excluding;
        
        if (dir.IsOpened())
        {
            wxString filename;
            
            bool cont = dir.GetFirst(&filename, wxT("*"), wxDIR_DIRS);
            while ( cont )
            {
                if (excluding.Index(filename) == wxNOT_FOUND)
                {
                    voices.Add(filename);
                }
                
                cont = dir.GetNext(&filename);
            }
        }
    }
    voices.Sort();

    return voices;
}

// Does this handler have the specified capability?
bool wxTTSCepstralHandler::HasCapability(wxTTSCapability capability) const
{
    if (capability == wxTTS_CAP_PROGRAM_LOCATION)
        return true;
    if (capability == wxTTS_CAP_DATA_LOCATION)
        return true;
    if (capability == wxTTS_CAP_WRITE_WAVE_FILE)
        return true;
    if (capability == wxTTS_CAP_SPEAK_HTML)
        return true;
    if (capability == wxTTS_CAP_VOICE_VARIATIONS)
        return false;
    if (capability == wxTTS_CAP_VOICE)
        return true;
    if (capability == wxTTS_CAP_PITCH)
        return true;
    if (capability == wxTTS_CAP_SPEED)
        return true;
    if (capability == wxTTS_CAP_VOLUME)
        return true;
    if (capability == wxTTS_CAP_SSML)
        return true;

    return false;
}

// Get the default voice
wxString wxTTSCepstralHandler::GetDefaultVoice() const
{
    wxArrayString voices = GetAvailableVoices();
    if (voices.GetCount() > 0)
        return voices[0];
    else
        return wxEmptyString;
}

// Get the default voice variant
wxString wxTTSCepstralHandler::GetDefaultVoiceVariant() const
{
    return wxT("None");
}
